#ifndef __SMARTPTR_H__
#define __SMARTPTR_H__

namespace inf {
    // this version eats up all instances where T doesn't have GetThisPtr enabled
    INF_INLINE void detail_assign_this( ... ) { }

    /*
    ============
    SmartPtr
    ============
    */
#ifdef _DEBUG
    template< class T, class CleanupPolicy = DefaultCleanupPolicy< T >, class CheckingPolicy = NULLPtrCheck< T > >
#else
    template< class T, class CleanupPolicy = DefaultCleanupPolicy< T >, class CheckingPolicy = NoPtrCheck< T > >
#endif
    class SmartPtr {
    public:
        typedef T& reference;
        typedef T* pointer;

        typedef const T& const_reference;
        typedef const T* const_pointer;

        typedef CheckingPolicy checking_policy;
        typedef CleanupPolicy cleanup_policy;

        SmartPtr( pointer p = NULL ) : pointee( p ), isWeak( false ) {
            count = new int(1);
            detail_assign_this( pointee, *this );						
        }

        explicit SmartPtr( NullPtr p ) : pointee( p ), isWeak( false ) {
            count = new int(1);
            detail_assign_this( pointee, *this );
        }

        SmartPtr( const SmartPtr& rhs ) :
        pointee( rhs.pointee ),
            count( rhs.count ),
            isWeak( false ) {

                if( !isWeak ) {
                    ++( *count );
                }
        }

        ~SmartPtr() {
            if( !isWeak ) {
                --( *count );
                if( ( *count ) == 0 ) {
                    cleanup_policy::free( pointee );
                    delete count;
                }
            }
            count	= NULL;
            pointee = NULL;
            isWeak	= true;
        }

        int				size() const {
            return *count;	
        }

        template< class U >
        bool friend		operator==( const pointer lhs, const SmartPtr< U >& rhs ) 	{ return lhs == rhs.pointee; }

        template< class U >
        bool friend		operator==( const SmartPtr< U >& lhs, const pointer rhs ) 	{ return lhs.pointee == rhs; }

        template< class U >
        bool friend		operator==( const SmartPtr< U >& lhs, const SmartPtr& rhs ) { return lhs.get() == rhs.pointee; }

        // operator !=
        template< class U >
        bool friend		operator!=( const pointer lhs, const SmartPtr< U >& rhs ) 	{ return lhs != rhs.pointee; }

        template< class U >
        bool friend		operator!=( const SmartPtr< U >& lhs, const pointer rhs ) 	{ return lhs.pointee != rhs; }

        template< class U >
        bool friend		operator!=( const SmartPtr& lhs, const SmartPtr< U >& rhs ) { return lhs.pointee != rhs.pointee; }


        // dereferencing
        pointer			operator->()			{ checking_policy::check( pointee ); return pointee; }
        const_pointer	operator->() const		{ checking_policy::check( pointee ); return pointee; }

        reference		operator*()				{ checking_policy::check( pointee ); return *pointee; }
        const_reference	operator*()	const		{ checking_policy::check( pointee ); return *pointee; }

        // accessors
        pointer			get()				{ return pointee; }
        const pointer	get() const			{ return pointee; }


        SmartPtr& 	operator=( const SmartPtr& rhs ) {
            SmartPtr( rhs ).swap( *this );
            return *this;
        }

        SmartPtr& 	reset( const pointer rhs ) {
            SmartPtr( rhs ).swap( *this );
            return *this;
        }

        SmartPtr& 	reset_weak( const SmartPtr& rhs ) {
            make_weak();
            assert( count == NULL );
            pointee = rhs.pointee;
            count = rhs.count;
            return *this;
        }

        void			swap( const SmartPtr& rhs ) {
            inf::iswap( isWeak, rhs.isWeak );
            inf::iswap( pointee, const_cast< pointer >( rhs.pointee ) );
            inf::iswap( count, const_cast< int* >( rhs.count ) );
        }
    protected:

        // we don't support this, use != NULL instead
        bool			operator!() const;

        void			make_weak() {
            assert( pointee == NULL || isWeak );
            if( !isWeak ) {
                delete count;
                count = NULL;
            }
            isWeak = true;
        }

    private:
        pointer							pointee;
        int*							count;
        mutable bool					isWeak;
    };

}   // ::inf

#endif // ! __SMARTPTR_H__
